import * as THREE from "three";

export class Utils {

    static compareAndSetMax(max: THREE.Vector3, other: THREE.Vector3) {
        if (other.x > max.x) {
            max.x = other.x;
        }
        if (other.y > max.y) {
            max.y = other.y;
        }
        if (other.z > max.z) {
            max.z = other.z;
        }
    }

    static compareAndSetMin(min: THREE.Vector3, other: THREE.Vector3) {
        if (other.x < min.x) {
            min.x = other.x;
        }
        if (other.y < min.y) {
            min.y = other.y;
        }
        if (other.z < min.z) {
            min.z = other.z;
        }
    }

    static recursive(obj: THREE.Object3D, callback: (item: THREE.Object3D) => void) {
        callback(obj);
        obj.children.forEach((item) => {
            Utils.recursive(item, callback);
        });

    }


    static getMeshes(result: Array<THREE.Mesh>, obj: THREE.Object3D) {
        if (obj instanceof THREE.Mesh) {
            result.push(obj);
        } else {
            for (let child of obj.children) {
                this.getMeshes(result, child);
            }
        }
    }


    static getVertices2(obj: THREE.Object3D): Float32Array {
        let result = new Array<THREE.Mesh>();
        Utils.getMeshes(result, obj);
        let size = 0;
        for (let mesh of result) {
            let attribue = mesh.geometry.attributes.position;
            size += attribue.count;

        }
        let points = new Float32Array(size);
        let offset = 0;
        for (let mesh of result) {
            let attribue = mesh.geometry.attributes.position;
            points.set(attribue.array, offset);
            offset += attribue.array.length;
        }
        return points;
    }


    static getVertices(obj: THREE.Object3D): Float32Array {
        let result = new Array<THREE.Mesh>();
        Utils.getMeshes(result, obj);
        let size = 0;
        for (let mesh of result) {
            let attribue = mesh.geometry.attributes.position;
            size += attribue.count;
        }
        let points = new Float32Array(size);
        let offset = 0;


        for (let mesh of result) {
            let attribue = mesh.geometry.attributes.position;
            for (let i = 0; i < attribue.array.length; i++) {
                points[offset + i] = attribue.array[i];
            }
            offset += attribue.array.length;
        }
        return points;
    }

    static computeBoundingBox(obj: THREE.Object3D): THREE.Box3 {
        let meshes = new Array<THREE.Mesh>();
        this.getMeshes(meshes, obj);
        let box = new THREE.Box3(new THREE.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE), new THREE.Vector3(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE));
        for (let m of meshes) {
            m.geometry.computeBoundingBox();
            let tb = m.geometry.boundingBox;
            if (tb) {
                Utils.compareAndSetMax(box.max, tb.max);
                Utils.compareAndSetMin(box.min, tb.min);
            }
        }
        if (box.min.x !== Number.MAX_VALUE) {
            return box;
        } else {
            const ZERO = new THREE.Vector3(0, 0, 0);
            return new THREE.Box3(ZERO, ZERO);
        }
    }

}


export class MeshBuilder {
    static createArrow(color: number) {
        const arrow = new THREE.Shape();
        arrow.moveTo(0, 0);
        arrow.lineTo(-1, 2);
        arrow.lineTo(1, 0);
        arrow.lineTo(-1, -2);
        const geometry = new THREE.ShapeGeometry(arrow);
        const material = new THREE.MeshBasicMaterial({ color });


        let mesh = new THREE.Mesh(geometry, material);

        return mesh;
    }

    static heart() {
        const x = 0, y = 0;
        const heartShape = new THREE.Shape();
        heartShape.moveTo(x + 5, y + 5);
        heartShape.bezierCurveTo(x + 5, y + 5, x + 4, y, x, y);
        heartShape.bezierCurveTo(x - 6, y, x - 6, y + 7, x - 6, y + 7);
        heartShape.bezierCurveTo(x - 6, y + 11, x - 3, y + 15.4, x + 5, y + 19);
        heartShape.bezierCurveTo(x + 12, y + 15.4, x + 16, y + 11, x + 16, y + 7);
        heartShape.bezierCurveTo(x + 16, y + 7, x + 16, y, x + 10, y);
        heartShape.bezierCurveTo(x + 7, y, x + 5, y + 5, x + 5, y + 5);

        const geometry = new THREE.ShapeGeometry(heartShape);
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const mesh = new THREE.Mesh(geometry, material);
        return mesh
    }


    static bar(aspect: number) {
        const arrow = new THREE.Shape();

        let top = -aspect * 3 / 7;
        let bottom = -aspect * 5 / 7;

        arrow.moveTo(-1, top);
        arrow.lineTo(1, top);
        arrow.lineTo(1, bottom);
        arrow.lineTo(-1, bottom);
        const geometry = new THREE.ShapeGeometry(arrow);
        const material = new THREE.MeshStandardMaterial({ color: 0, transparent: true, opacity: 0.2 });
        let mesh = new THREE.Mesh(geometry, material);
        return mesh;
    }
}